//
// Created by Ivor_ on 2024/2/5.
//

#include "deQueue.h"

DQ_NODE* createNodeDQ(const void* _data, size_t dataSize, DQ_NODE* _last, DQ_NODE* _next) {
    DQ_NODE* newNode = (DQ_NODE*)malloc(sizeof(DQ_NODE));
    newNode->data = malloc(dataSize + 1);
    memset(newNode->data, 0, dataSize + 1);
    memcpy(newNode->data, _data, dataSize), newNode->last = _last, newNode->next = _next;
    return newNode;
}

DEQUEUE* createDequeueDQ() {
    DEQUEUE* newList = (DEQUEUE*)malloc(sizeof(DEQUEUE));
    newList->len = 0, newList->top = NULL, newList->bottom = NULL;
    return newList;
}

void freeNodeDQ(DQ_NODE* nodeObj) {
    free(nodeObj->data);
    free(nodeObj);
}

void freeDequeueDQ(DEQUEUE* dqObj) {
    for (int i = 0; i < dqObj->len; ++i) {
        DQ_NODE* temp = dqObj->top;
        dqObj->top = dqObj->top->last;
        freeNodeDQ(temp);
    }
    free(dqObj);
}

void pushTopDQ(const void* _data, size_t dataSize, DEQUEUE* dqObj) {
    if (!(dqObj->len)) {
        dqObj->len = 1;
        dqObj->top = dqObj->bottom = createNodeDQ(_data, dataSize, NULL, NULL);
        return;
    }
    (dqObj->len) ++;
    dqObj->top->next = createNodeDQ(_data, dataSize, dqObj->top, NULL);
    dqObj->top = dqObj->top->next;
}

void pushBottomDQ(const void* _data, size_t dataSize, DEQUEUE* dqObj) {
    if (!(dqObj->len)) {
        dqObj->len = 1;
        dqObj->top = dqObj->bottom = createNodeDQ(_data, dataSize, NULL, NULL);
        return;
    }
    (dqObj->len) ++;
    dqObj->bottom->last = createNodeDQ(_data, dataSize, NULL, dqObj->bottom);
    dqObj->bottom = dqObj->bottom->last;
}

void* popTopDQ(DEQUEUE* dqObj, size_t dataSize) {
    if (!(dqObj->len)) {
        fprintf(stderr, "Pop failed, cannot pop a top element from an empty dequeue!\n");
        return NULL;
    }
    dqObj->len --;
    void* ret = malloc(dataSize + 1);
    memcpy(ret, dqObj->top->data, dataSize);
    if (!(dqObj->len)) {
        freeNodeDQ(dqObj->top);
        dqObj->top = dqObj->bottom = NULL;
    } else {
        dqObj->top = dqObj->top->last;
        freeNodeDQ(dqObj->top->next);
        dqObj->top->next = NULL;
    }
    return ret;
}

void* popBottomDQ(DEQUEUE* dqObj, size_t dataSize) {
    if (!(dqObj->len)) {
        fprintf(stderr, "Pop failed, cannot pop a bottom element from an empty dequeue!\n");
        return NULL;
    }
    dqObj->len --;
    void* ret = malloc(dataSize + 1);
    memcpy(ret, dqObj->bottom->data, dataSize);
    if (!(dqObj->len)) {
        freeNodeDQ(dqObj->bottom);
        dqObj->bottom = dqObj->top = NULL;
    } else {
        dqObj->bottom = dqObj->bottom->next;
        freeNodeDQ(dqObj->bottom->last);
        dqObj->bottom->last = NULL;
    }
    return ret;
}

void* peekTopDQ(const DEQUEUE* dqObj, size_t dataSize)  {
    if (!(dqObj->len)) {
        fprintf(stderr, "Peek warning, cannot peek a valid top element from an empty dequeue!\n");
        return NULL;
    }
    void* ret = malloc(dataSize + 1);
    memcpy(ret, dqObj->top->data, dataSize);
    return ret;
}

void* peekBottomDQ(const DEQUEUE* dqObj, size_t dataSize) {
    if (!(dqObj->len)) {
        fprintf(stderr, "Peek warning, cannot peek a valid bottom element from an empty dequeue!\n");
        return NULL;
    }
    void* ret = malloc(dataSize + 1);
    memcpy(ret, dqObj->bottom->data, dataSize);
    return ret;
}
